package jp.sourceforge.akjrcp.text_douga.item;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;

import jp.sourceforge.akjrcp.draw.DecoratedShapePainter;
import jp.sourceforge.akjrcp.draw.TextLinesLayout;
import jp.sourceforge.akjrcp.text_douga.DougaPreference;
import jp.sourceforge.akjrcp.text_douga.DougaSystem;
import jp.sourceforge.akjrcp.text_douga.ImageObject;

import org.apache.commons.lang.StringUtils;

/*
 * normal image
 */
public class TextItem extends AbstractItem{
	
	
	protected String text;
	
	
	protected Font font=new Font(DEFAULT_FONT_NAME,DEFAULT_FONT_STYLE,DEFAULT_FONT_SIZE);
	
	//private static BufferedImage tmpBuffer=new BufferedImage(1,1,BufferedImage.TYPE_BYTE_GRAY);
	//private static Graphics2D tmpG2=(Graphics2D) tmpBuffer.getGraphics();

	
//	private Shape[] textShapes;
//	private String[] textLines;

	protected DecoratedShapePainter decoratedShapePainter=new DecoratedShapePainter();
	
	//TODO FONT
	public static final String DEFAULT_FONT_NAME="MS Mincho";
	public static final int DEFAULT_FONT_SIZE=18;
	public static final int DEFAULT_FONT_STYLE=Font.PLAIN;
	
	protected int text_align=TextItem.LEFT;//TODO can set;
	protected int text_margin=6;//TODO can set;
	
	protected TextLinesLayout textLinesLayout;
	protected Rectangle textRect;
	
	private int firstOffsetX=0;//for MultiStyledText
	private int firstLineHeight=0;
	private Point lastDrawPosition;//for MultiStyledText;
	private int lastLineY;
	private int maxDrawedX;
	
	private boolean debug=false;
	public TextItem(TextItem base,String text){
		super(base);
		this.text=text;
		this.font=base.getFont();//share
		this.text_align=base.text_align;
		this.text_margin=base.text_margin;	
		try {
			this.decoratedShapePainter=(DecoratedShapePainter) base.decoratedShapePainter.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
	}
	
	public TextItem(String text){
		this.text=text;
		setAlign(CENTER);
		setValign(BOTTOM);
		setFadeInTime(0);
		setFadeOutTime(0.3);
	}
	
	public String toString(){
		return "TextItem:"+super.toString()+" text="+text+" font="+font;
	}
	
	public TextItem(){
		this("");
	}
	
	public void beforeWriteFrame(double time){
		
	}
	public boolean writeFrame(DougaPreference preference,ImageObject baseImage,int frame)throws IOException{
		boolean written=false;
		if(duration==DougaSystem.FOREEVER){//maybe this is background image
			written=true;
		}
		double frameTime=(double)(frame-1)/preference.getFps();//to change start 0
		if(written==false){
			written=isInFrame(preference,frame);
			}
		
		
		if(written==true){
			
			//TODO wrap text. use LineTextData.class or wrapString()
			//Rectangle stringRect=getStringShape(baseImage.getImage()).getBounds();
			Graphics2D g2=(Graphics2D) baseImage.getImage().getGraphics();
			g2.setFont(font);
			//drawing;
			
			
			
			//g2.drawRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height);//debug
			
			
	
			initializeShape(g2,preference);
			beforeWriteFrame(frameTime-startTime);
			//ALPAH
			float transparent=getFrameTransparent(preference,frameTime);
			//log.trace("frame="+frame+",frameTime="+frameTime+",transparent="+transparent);
			g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparent));//use tp!
			log.trace(font);
			Rectangle drawRect=getDrawRect(preference,baseImage.getImage());
			if(drawRect==null){
				
				return false;
			}
			if(debug){
			//the bound little bit strange.
			g2.setColor(new Color(255,0,0));
			g2.drawRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height);//debug
			}
			
			log.trace("drawrect:"+drawRect);
			//System.out.println("des:"+g2.getFontMetrics().getDescent()+","+g2.getFontMetrics().getAscent());
			//System.out.println("movex:"+getMovedX(preference,frame)+","+getMovedY(preference,frame));
			int dx=drawRect.x+getMovedX(preference,frameTime);
			int dy=drawRect.y+g2.getFontMetrics().getAscent()+getMovedY(preference,frameTime);//
			//System.out.println("size:"+font.getSize()+","+textRect.height);
			//int dy=drawRect.y-(drawRect.height-textRect.height)/2+font.getSize();//
			log.trace("translate dx="+dx+",dy="+dy+","+g2);
			g2.translate(dx,dy);//for shape.
			Shape[] textShapes=textLinesLayout.getShapes();
			
			//log.trace("shapes:"+textShapes.length);
			//TextLayout[] layouts=textLinesLayout.getTextLayouts();
			
			
			AffineTransform affin=new AffineTransform();
			//System.out.println(shearX+","+shearY+","+rotate+","+moveRotate);
			
			
			if(shearX>0|| shearY>0)
				g2.shear(shearX, shearY);
			int movedRotate=getMovedRotate(preference,frameTime);
			if(movedRotate>0){
				affin.setToRotation(movedRotate*Math.PI/180,textRect.width/2,textRect.height/2-g2.getFontMetrics().getAscent());
				//affin.translate(-textRect.width/2, -textRect.height/2);
				//g2.transform(affin);
				//g2.rotate(rotate*Math.PI/180);
			}
			g2.transform(affin);
			
			//lastLineY=0;
			//maxDrawedX=0;
			
			int lx=dx;
			int ly=drawRect.y;
			for (int i = 0; i < textShapes.length; i++) {
				if(i==0){
					g2.translate(firstOffsetX, 0);
					lx+=firstOffsetX;
				}else{
					lx=dx;
				}
				decoratedShapePainter.setSelectionShape(getSelectionShape(i));
				//text align left
				log.trace("i="+i+" dx="+dx+" dy="+dy+",shape="+textShapes[i].getBounds());
				decoratedShapePainter.paint(g2, textShapes[i]);
				if(lx+textShapes[i].getBounds().width>maxDrawedX){
					
					maxDrawedX=lx+textShapes[i].getBounds().width;
					
				}
				
				int moveY=lastLineY-drawRect.y;
				if(font.getSize()+text_margin>moveY){
					moveY=font.getSize()+text_margin;
					}
				g2.translate(0, Math.max(firstLineHeight,moveY));//do line change
				
				if(i==0){//offset effect only first line.
					g2.translate(-firstOffsetX, 0);
				}
				if(i!=textShapes.length-1){
					ly+=font.getSize()+text_margin;
					if(lastLineY>ly){
						log.info("over Y:");
						ly=lastLineY;
					}
				}else{//last line
					lx+=textShapes[i].getBounds().x+textShapes[i].getBounds().width;
					
					if(text.endsWith(" ") || text.endsWith("\t")){
						lx+=font.getSize()/2;//add space //TODO much support white spaces
					}
				}
			}
			
			//int dx=(drawRect.width-getStringShape(baseImage.getImage()).getBounds().width)/2+drawRect.x-stringRect.x;
			//int dy=(drawRect.height-font.getSize())/2+drawRect.y-stringRect.y;//shape
			lastDrawPosition=new Point(lx,ly);
			
			
		}
		
		return written;
	}
	
	
	//for subclass
	public Shape getSelectionShape(int index){
		//layouts[i].getLogicalHighlightShape(0, 1)
		return null;
	}
	
	private int getDrawTextWidth(DougaPreference preference){
		int w=getWidth();
		if(w==0){
			if(x==0){//for default layout
				w=preference.getWidth(); 
			}else{
				w=9999;//no wrap
			}
			
			}
		return w;
	}
	public void clear(){
		textLinesLayout=null;
		
		textRect=null;
	}
	public void initializeShape(Graphics2D g2,DougaPreference preference){
		if(textLinesLayout!=null){
			return;
		}
		int maxWidth=getDrawTextWidth(preference);//for wrap text ,if no wrap set large width
		textLinesLayout=new TextLinesLayout(g2,font,transformText(text),maxWidth-firstOffsetX,maxWidth);
		textRect=textLinesLayout.getBounds();
	}
	
	public String transformText(String text){
		String out=StringUtils.replace(text,"\\n", "\n");
		return out;
	}
	/*
	 * old and english only
	 */
	/*
	public String[] wrapString(FontMetrics metrics,int maxwidth,String str){
		List<String> arrayList=new ArrayList<String>();
		String dummy_ret="__DUMMY_RETURN__";
		String value=StringUtils.replace(str,"\r\n","\n");
        value=StringUtils.replace(value,"\r","\n");
        value=StringUtils.replace(value, "\\\\n", dummy_ret);
		value=StringUtils.replace(value,"\\n","\n");
		String lines[]=str.split("\n");//split by line
		String newLine="";
		for(int i=0;i<lines.length;i++){
			lines[i]=StringUtils.replace(str,dummy_ret,"\\\\n");//back dummy \n for print
			  String words[]=lines[i].split(" ");
			            String tmpLine="";
			               for(int k=0;k<words.length;k++){
			                   tmpLine+=words[k];
			                   if(metrics.stringWidth(tmpLine)>maxwidth){
			                   if(newLine.length()>0){
			                	   arrayList.add(newLine);
				                    newLine="";
				                    tmpLine=words[k];
			                   		}else{
			                   		 for(int j=0;j<words[k].length();j++){
			                   		     tmpLine+=words[k].charAt(j);
			           	               if(metrics.stringWidth(newLine)>maxwidth){
			           	            	arrayList.add(tmpLine);
			           	                   tmpLine="";
			           	                   newLine="";
			           	               }else{
			           	                   newLine=tmpLine;
			           	               }
			           	           }
			                   		}
			                   }else{
			                       tmpLine=tmpLine+" ";
			                       newLine=tmpLine;
			                   }
			               }
			               
			           
			           if(tmpLine.length()>0){
			        	   arrayList.add(tmpLine);
			               
			           }
			           newLine="";
			        }
	return arrayList.toArray(new String[arrayList.size()]);
	}
	*/
	/*
	public static Shape makeStringShape(String text,Font font,BufferedImage dstImage){
		
	Graphics2D g2 = (Graphics2D)dstImage.getGraphics();
	TextLayout textLayout =
	  new TextLayout(text, font, g2.getFontRenderContext());
	Shape shape = textLayout.getOutline(new AffineTransform());
	g2.dispose();
	return shape;
	}
	
	public static WrappedText makeStringShapes(String text,Font font,BufferedImage dstImage,int maxwidth){
		String lines[]=TextDougaTextUtils.wrapText(text, font, maxwidth);
		Shape[] shapes=new Shape[lines.length];
		for (int i = 0; i < shapes.length; i++) {
			shapes[i]=makeStringShape(lines[i],font,dstImage);
		}
		WrappedText wrappedText=new WrappedText();
		wrappedText.setTexts(lines);
		wrappedText.setShapes(shapes);
		return wrappedText;
	}
	
	public static class WrappedText{
		private String texts[];
		private Shape shapes[];
		public Shape[] getShapes() {
			return shapes;
		}
		public void setShapes(Shape[] shapes) {
			this.shapes = shapes;
		}
		public String[] getTexts() {
			return texts;
		}
		public void setTexts(String[] texts) {
			this.texts = texts;
		}
	}
	*/
	

	

	/**
	 * no more baseImage
	 * for get x,y
	 */
	public Rectangle getDrawRect(DougaPreference preference,BufferedImage baseImage){
		if(text==null ){
			log.warn("text is null");
			//|| 
			return null;
		}
		
		if(text.length()==0){
			log.warn("text length=0");
			return null;
		}
		
		
		int drawwidth=Math.max(textRect.width,width);
		int drawheight=Math.max(textRect.height,height);
		//draw width is text size or setted size.
		log.trace("text:"+drawwidth+","+drawheight);
		//Rectangle textRect=tmpG2.getFontMetrics(font).getStringBounds(text, context)
		int dw=drawwidth;
		int dh=drawheight;
		
		int dx=0;
		if(align==NONE){
			dx=x;
			dw=drawwidth;
		}else{
			if(align==LEFT){
				if(x==0 && width==0){//auto set
					dx=marginLeft;
				}else{
					//for box 0,0,pref.width,pref.height
					dx=x;
				}
				dw=drawwidth;
			}else if(align==RIGHT){
				if(x==0 && width==0){
					dx=preference.getWidth()-textRect.width-marginRight;
				}else{
					dx=x+(drawwidth-textRect.width);
				}
				dw=drawwidth;
			}else if(align==CENTER){
				if(width==0){
					dx=marginRight+(preference.getWidth()-marginLeft-marginRight-dw)/2;
				}else{
					dx=x+(drawwidth-textRect.width)/2;
					
				}
				dw=drawwidth;
			}else{
				log.warn("invalid align:"+align);
			}
		}
		int dy=0;
		//here just make rectangle
		if(valign==NONE){
			dy=y;
			dh=drawheight;
		}else{
			if(valign==TOP){
				if(y==0 && height==0){//auto margin
					dy=y+marginTop;
				}else{
					dy=y;
				}
				dh=drawheight;
			}else if(valign==BOTTOM){
				if(y==0 && height==0){
					dy=preference.getHeight()-textRect.height-marginBottom;
				}else{
					dy=y+(drawheight-textRect.height);
					
				}
				dh=drawheight;
			}else if(valign==MIDDLE){
				if(height==0){
					dy=(preference.getHeight()-textRect.height-marginBottom)/2;
				}else{
					dy=y+(drawheight-textRect.height)/2;
					
				}
				dh=drawheight;
			}else{
				log.warn("invalid valign:"+valign);
			}
		}
		return new Rectangle(dx,dy,dw,dh);//just use dx,dy?
	}

	public Font getFont() {
		return font;
	}


	public void setFont(Font font) {
		this.font = font;
	}


	public String getText() {
		return text;
	}


	public void setText(String text) {
		this.text = text;
	}

	public DecoratedShapePainter getDecoratedShapePainter() {
		return decoratedShapePainter;
	}

	public void setDecoratedShapePainter(DecoratedShapePainter decoratedShapePainter) {
		this.decoratedShapePainter = decoratedShapePainter;
	}

	public Rectangle getTextRect() {
		return textRect;
	}

	
	/*
	 * below code for MultiStyledText
	 * TODO
	 * I must refactor these things.
	 */
	protected int getFirstOffset() {
		return firstOffsetX;
	}

	protected void setFirstOffset(int firstOffset) {
		this.firstOffsetX = firstOffset;
	}

	protected Point getLastDrawPosition() {
		return lastDrawPosition;
	}

	protected int getLastLineY() {
		return lastLineY;
	}

	protected void setLastLineY(int lastLineHeight) {
		this.lastLineY = lastLineHeight;
	}

	protected int getFirstLineHeight() {
		return firstLineHeight;
	}



	protected int getLineHeight() {
		return font.getSize()+text_margin;
	}

	protected int getMaxDrawedX() {
		return maxDrawedX;
	}

	public void setMaxDrawedX(int maxDrawedX) {
		this.maxDrawedX = maxDrawedX;
	}

	public int getFirstOffsetX() {
		return firstOffsetX;
	}

	public void setFirstOffsetX(int firstOffsetX) {
		this.firstOffsetX = firstOffsetX;
	}

	public void setFirstLineHeight(int firstLineHeight) {
		this.firstLineHeight = firstLineHeight;
	}

	public void setLastDrawPosition(Point lastDrawPosition) {
		this.lastDrawPosition = lastDrawPosition;
	}


	public void resetMultiStyledSupport(){
		setMaxDrawedX(0);
		setLastLineY(0);
		setFirstOffset(0);
		lastDrawPosition=null;
		firstLineHeight=0;
		firstOffsetX=0;
	}



}
